/*
 * 
 * FluentNHibernate.LowercaseSystem version 1.0
 *  
 * Lowercase naming system plus robust checking/mapping of joining-field between collections and references
 *  
 * Solves the impedance mismatch between ORM name casings and database tables,columns casings; typically Postgresql.
 * 
 * Author: Michael Buen (anagram of IEnableMuch)
 * 
 * Last modified: Dec 27, 2010
 * 
 * Note: Can also be used on non-lowercase system, pass false to toLowercase parameter; doing so, 
 * you can still get the benefits of robust checking/mapping of joining-field between collections and references,
 * courtesy of NormalizeReference.
 * 
 */


 

using System;
 
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
 
using NHibernate;
using NHibernate.Dialect;
 
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db; 
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
using FluentNHibernate.Conventions.Helpers;
using FluentNHibernate.Mapping;
using FluentNHibernate.Mapping.Providers;
 
 



namespace FluentNHibernate.LowercaseSystem
{
 


    public static class LowercaseForeignKey
    {
        public static ForeignKeyConvention EndsWith(string suffix)
        {
            return EndsWith(suffix, true);
        }

        public static ForeignKeyConvention EndsWith(string suffix, bool toLowercase)
        {
			
			
			
            return new BuiltSuffixLowercaseForeignKeyConvention(suffix, toLowercase);
        }

    }



    public class BuiltSuffixLowercaseForeignKeyConvention : ForeignKeyConvention
    {
        private readonly string suffix;

        private readonly bool toLowercase;

        public BuiltSuffixLowercaseForeignKeyConvention(string suffix, bool toLowercase)
        {
            this.suffix = suffix;
            this.toLowercase = toLowercase;
        }

        public BuiltSuffixLowercaseForeignKeyConvention(string suffix) : this(suffix,true) {}

        protected override string GetKeyName(Member property, Type type)
        {
            return (property != null ? 
                property.Name.ToLowercaseNamingConvention(this.toLowercase) 
                :
                type.Name.ToLowercaseNamingConvention(this.toLowercase)) + suffix;
        }
    }

 
    public static class Helper
    {
        

        
        
        public static FluentMappingsContainer AddFromNamespaceOf<T>(this FluentMappingsContainer fmc)
        {
            Type t = typeof(T);
            string ns = t.Namespace;
            
        
            var y = from x in t.Assembly.GetTypes()
                    where 
                        x.FullName == ns + "." + x.Name
                        && x.BaseType.IsGenericType
                        // && (x.BaseType.GetGenericTypeDefinition() == (typeof(ClassMap<>)
                        //  || x.BaseType.GetGenericTypeDefinition() == typeof(ClassMapExt<>))
                        && x.BaseType.GetGenericTypeDefinition().IsDerivedFrom(typeof(ClassMap<>))
                    select x;
                    
            foreach(Type z in y)
                fmc.Add(z);                 
        
            
            return fmc;
        }
		
		

        private static bool IsDerivedFrom(this Type givenType, Type genericType)
        {
            if (givenType == genericType)
                return true;
            
            if (givenType.BaseType.IsGenericType)
                return givenType.BaseType.GetGenericTypeDefinition().IsDerivedFrom(genericType);
            
            return false;
        }


        public static T ForLowercaseSystem<T>(this SetupConventionFinder<T> fluentMappingsContainer)
        {
            return fluentMappingsContainer.ForLowercaseSystem("_id", true);
        }

		public static T ForLowercaseSystem<T>(this SetupConventionFinder<T> fluentMappingsContainer, bool toLowercase)
        {
            return fluentMappingsContainer.ForLowercaseSystem("_id", toLowercase);
        }
 
        public static T ForLowercaseSystem<T>(this SetupConventionFinder<T> fluentMappingsContainer, string referenceSuffix)
        {               
            return fluentMappingsContainer.ForLowercaseSystem(referenceSuffix, true);
        }
		

		
 
        public static T ForLowercaseSystem<T>(this SetupConventionFinder<T> fluentMappingsContainer, string referenceSuffix, bool toLowercase)
        {
            
 
            IList<IConvention> lcase =
                new IConvention[]
                {
                    Table.Is(x => x.EntityType.Name.ToLowercaseNamingConvention(toLowercase))
                    ,ConventionBuilder.Property.Always(x => x.Column(x.Name.ToLowercaseNamingConvention(toLowercase)))
                    ,ConventionBuilder.Id.Always( x => x.Column(x.Name.ToLowercaseNamingConvention(toLowercase)) )        
				
				    // opt-in, doesn't work on POCOs and mappers that are in separate assemblies
                    // ,ConventionBuilder.HasMany.Always(x => x.Key.Column( x.NormalizeReference().ToLowercaseNamingConvention(toLowercase) + referenceSuffix )  )
                 
                    // Instead of this...
                    // ,ForeignKey.EndsWith(referenceSuffix)                                
                    // ... we do this, so we have direct control on Reference name's casing:
                    , LowercaseForeignKey.EndsWith(referenceSuffix, toLowercase)

                    // deprecated in favor of LowercaseForeignKey helper
                    // ,ConventionBuilder.Reference.Always(x => x.Column( x.Name.ToLowercaseNamingConvention(toLowercase) + referenceSuffix ) )
                    
                    
                 
                };
 
            return fluentMappingsContainer.Add(lcase.ToArray());
 
            // return fluentMappingsContainer.Add(otherConventions);
        }
 
 
        public static string ToLowercaseNamingConvention(this string s)
        {
            return s.ToLowercaseNamingConvention(true);
        }
 
        public static string ToLowercaseNamingConvention(this string s, bool toLowercase)
        {
            if (toLowercase)
            {
                var r = new Regex(@"
                (?<=[A-Z])(?=[A-Z][a-z]) |
                 (?<=[^A-Z])(?=[A-Z]) |
                 (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
 
                return r.Replace(s, "_").ToLower();
            }
            else
                return s;
        }
		
		

		

 
 
    }// class Helper
}// namespace FluentNHibernate.LowercaseSystem